#!/usr/bin/env python
"""
##############################################################################
用法：“./cpall.py dirFrom dirTo [-m maxFileLoad] [-b blockSize] [-v verbose]”。

递归地复制dirFrom到dirTo下。和Unix命令“cp -r dirFrom dirTo”效果类似，其中假定dirFrom
和dirTo都是目录。
##############################################################################
"""

import os
import sys
import time

MAXFILELOAD = 1024 * 1024
BLOCKSIZE = 1024 * 1024
VERBOSE = 0


def copyFile(
    pathFrom,
    pathTo,
    maxFileLoad=MAXFILELOAD,
    blockSize=BLOCKSIZE
):
    """
    将单个文件逐字节的从pathFrom复制到pathTo。
    使用二进制文件模式防止Unicode解码及换行符转换。
    """

    if os.path.getsize(pathFrom) <= maxFileLoad:
        open(pathTo, 'wb').write(open(pathFrom, 'rb').read())
    else:
        fileFrom = open(pathFrom, 'rb')
        fileTo = open(pathTo, 'wb')
        while True:
            bytesFrom = fileFrom.read(blockSize)
            if not bytesFrom:
                break
            fileTo.write(bytesFrom)
        fileFrom.close()
        fileTo.close()


def copyTree(
    dirFrom,
    dirTo,
    maxFileLoad=MAXFILELOAD,
    blockSize=BLOCKSIZE,
    verbose=VERBOSE
):
    """
    将dirFrom下的内容递归地复制到dirTo，返回(文件数目，目录数目)形式的元组。
    为避免在某些平台上目录吗不可解码，可能需要为其名使用字节。
    """

    fileCount = dirCount = 0

    for filename in os.listdir(dirFrom):
        pathFrom = os.path.join(dirFrom, filename)
        pathTo = os.path.join(dirTo, filename)

        if not os.path.isdir(pathFrom):
            try:
                # print('verbose ->', verbose)
                if verbose > 1:
                    print('文件：正在将{}复制到{}'.format(pathFrom, pathTo))
                copyFile(pathFrom, pathTo, maxFileLoad, blockSize)
                fileCount += 1
            except Exception:
                print('错误：将{}复制到{}，已跳过'.format(pathFrom, pathTo))
                print(sys.exc_info()[0], sys.exc_info()[1])

        else:
            if verbose:
                print('目录：正在将{}复制到{}'.format(pathFrom, pathTo))
            try:
                os.mkdir(pathTo)
            except Exception:
                print('错误：创建{}，已跳过'.format(pathTo))
                print(sys.exc_info()[0], sys.exc_info()[1])
            below = copyTree(pathFrom, pathTo, maxFileLoad, blockSize, verbose)
            fileCount += below[0]
            dirCount += below[1]
            dirCount += 1

    return fileCount, dirCount


def getInt(label):
    index = sys.argv.index(label) + 1

    try:
        var = int(sys.argv[index])
    except IndexError:
        print('错误：命令行参数{}之后无数字')
        sys.exit()
    except ValueError:
        print('错误：命令行参数{}之后不是数字')
        sys.exit()
    else:
        return var


def getArgs(
    maxFileLoad=MAXFILELOAD,
    blockSize=BLOCKSIZE,
    verbose=VERBOSE
):
    """
    获取并验证文件命令行参数
    """
    if len(sys.argv) < 3:
        msg = '使用方法：'
        msg += '\n./cpall.py dirFrom dirTo '
        msg += '[-m maxFileLoad] [-b blockSize] [-v verbose]'
        print(msg)
        sys.exit()

    dirFrom, dirTo = sys.argv[1:3]
    dirFrom, dirTo = map(os.path.abspath, [dirFrom, dirTo])

    if not os.path.isdir(dirFrom):
        print('错误：dirFrom不是一个目录')
        sys.exit()

    if os.path.isfile(dirTo):
        print('错误：dirTo不是一个目录')
        sys.exit()

    dirTo = os.path.join(dirTo, os.path.basename(dirFrom))

    if not os.path.exists(dirTo):
        os.mkdir(dirTo)
        print('提示：创建了dirTo')
    else:
        print('警告：dirTo已存在')

    if hasattr(os.path, 'samefile'):
        isSame = os.path.samefile(dirFrom, dirTo)
    else:
        isSame = os.path.abspath(dirFrom) == os.path.abspath(dirTo)

    if isSame:
        print('错误：dirFrom和dirTo是同一个目录')
        sys.exit()

    if '-m' in sys.argv:
        maxFileLoad = getInt('-m')
    if '-b' in sys.argv:
        blockSize = getInt('-b')
    if '-v' in sys.argv:
        verbose = getInt('-v')

    return dirFrom, dirTo, maxFileLoad, blockSize, verbose


def command():
    args = getArgs()
    print('参数：')
    names = ('dirFrom', 'dirTo', 'maxFileLoad', 'blockSize', 'verbose')
    for index in range(len(names)):
        print('\t{} -> {}'.format(names[index], args[index]))
    print('正在复制')
    start = time.time()
    fcount, dcount = copyTree(*args)
    finish = time.time()
    print('复制完成：用时{}s，共复制了{}个文件、{}个目录'.format(finish - start, fcount, dcount))


if __name__ == '__main__':
    command()
